/***************************************************************************//**
 * @file CGI/DataTypeSkyDome.cpp
 * @author  Marek M. Cel <marekcel@mscsim.org>
 *
 * @section LICENSE
 *
 * Copyright (C) 2014 Marek M. Cel
 *
 * This file is part of MScSim. You can redistribute and modify it under
 * the terms of GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Further information about the GNU General Public License can also be found
 * on the world wide web at http://www.gnu.org.
 ******************************************************************************/
#ifndef DATA_TYPE_SKY_DOME_CPP
#define DATA_TYPE_SKY_DOME_CPP
#endif

////////////////////////////////////////////////////////////////////////////////

#include <osg/Geode>
#include <osg/Texture2D>

#include <osgDB/ReadFile>

#include <CGI/VisitorFindNode.h>
#include <CGI/DataTypeSkyDome.h>

////////////////////////////////////////////////////////////////////////////////

using namespace std;

////////////////////////////////////////////////////////////////////////////////

DataTypeSkyDome::DataTypeSkyDome( osg::Node *node )
{
#   ifndef MSCSIM_OSGEPHEMERIS
    m_elevation = 0.0;

    m_textureId = 0;
    m_textureId_prev = 0;

    m_textureDay   = false;
    m_textureNight = false;

    m_textures.clear();

    m_textures.push_back( string( "data/textures/skydome/sky_twilight_05.png" ) ); // 0
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_10.png" ) ); // 1
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_15.png" ) ); // 2
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_20.png" ) ); // 3
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_25.png" ) ); // 4
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_30.png" ) ); // 5
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_35.png" ) ); // 6
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_40.png" ) ); // 7
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_45.png" ) ); // 8
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_50.png" ) ); // 9
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_55.png" ) ); // 10
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_60.png" ) ); // 11
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_65.png" ) ); // 12
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_70.png" ) ); // 13
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_75.png" ) ); // 14
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_80.png" ) ); // 15
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_85.png" ) ); // 16
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_90.png" ) ); // 17
    m_textures.push_back( string( "data/textures/skydome/sky_twilight_95.png" ) ); // 18
#   else
    memset( &m_dateTime      , 0, sizeof(Utils::DateTime) );
    memset( &m_dateTime_prev , 0, sizeof(Utils::DateTime) );
#   endif

    VisitorFindNode findPosition( "SkyDomePosition" );
    VisitorFindNode findSizeCoef( "SkyDomeSizeCoef" );
    VisitorFindNode findAttitude( "SkyDomeAttitude" );

    node->accept( findPosition );
    node->accept( findSizeCoef );
    node->accept( findAttitude );

    m_skyDomePosition = dynamic_cast<osg::PositionAttitudeTransform *>( findPosition.getFirstNode().get() );
    m_skyDomeSizeCoef = dynamic_cast<osg::PositionAttitudeTransform *>( findSizeCoef.getFirstNode().get() );
    m_skyDomeAttitude = dynamic_cast<osg::PositionAttitudeTransform *>( findAttitude.getFirstNode().get() );

#   ifndef MSCSIM_OSGEPHEMERIS
    VisitorFindNode findSkyDomeGeode( "SkyDomeGeode" );

    node->accept( findSkyDomeGeode );

    m_skyDomeGeode = dynamic_cast<osg::Geode *>( findSkyDomeGeode.getFirstNode().get() );
#   else
    VisitorFindNode findEphemeris( "SkyDomeEphemeris" );

    node->accept( findEphemeris );

    m_ephemerisModel = dynamic_cast<osgEphemeris::EphemerisModel *>( findEphemeris.getFirstNode().get() );
#   endif
}

////////////////////////////////////////////////////////////////////////////////

void DataTypeSkyDome::update()
{
    updatePosition();
    updateScale();
    updateAttitude();
#   ifndef MSCSIM_OSGEPHEMERIS
    updateTexture();
#   else
    updateEphemeris();
#   endif
}

////////////////////////////////////////////////////////////////////////////////

void DataTypeSkyDome::updatePosition()
{
    if ( m_skyDomePosition.valid() )
    {
        m_skyDomePosition->setPosition( m_position );
    }
    else
    {
        exit( -1 );
    }
}

////////////////////////////////////////////////////////////////////////////////

void DataTypeSkyDome::updateScale()
{
    if ( m_skyDomeSizeCoef.valid() )
    {
        m_skyDomeSizeCoef->setScale( m_sizeCoef );
    }
    else
    {
        cerr << "ERROR! Wrong node pointer." << endl;
    }
}

////////////////////////////////////////////////////////////////////////////////

void DataTypeSkyDome::updateAttitude()
{
    if ( m_skyDomeAttitude.valid() )
    {
        m_skyDomeAttitude->setAttitude( m_attitude );
    }
    else
    {
        cerr << "ERROR! Wrong node pointer." << endl;
    }
}

#ifndef MSCSIM_OSGEPHEMERIS
////////////////////////////////////////////////////////////////////////////////

void DataTypeSkyDome::updateTexture()
{
    m_textureDay   = false;
    m_textureNight = false;

    if ( m_skyDomeGeode.valid() )
    {
        double elevation_deg = osg::RadiansToDegrees( m_elevation );

        if ( elevation_deg >= -12.0 && elevation_deg < 12.0 )
        {
            m_textureId = floor( 19.0 * ( elevation_deg + 12.0 ) / 24.0 );

            if ( m_textureId <  0 ) m_textureId =  0;
            if ( m_textureId > 18 ) m_textureId = 18;
        }
        else
        {
            if ( elevation_deg < -12.0 )
            {
                m_textureNight = true;
                m_textureId = -100;
            }
            else
            {
                m_textureDay = true;
                m_textureId = -200;
            }
        }

        if ( m_textureId != m_textureId_prev )
        {
            osg::ref_ptr<osg::StateSet> skyDomeStateSet = m_skyDomeGeode->getOrCreateStateSet();

            std::string textureFile;

            // texture
            if ( m_textureNight || m_textureDay )
            {
                if ( m_textureNight )
                {
                    textureFile = "data/textures/skydome/sky_night.png";
                }
                else
                {
                    textureFile = "data/textures/skydome/sky_day.png";
                }
            }
            else
            {
                textureFile = m_textures.at( m_textureId );
            }

            osg::ref_ptr<osg::Image> skyDomeImage = osgDB::readImageFile( textureFile );

            if ( skyDomeImage.valid() )
            {
                osg::ref_ptr<osg::Texture2D> skyDomeTexture = new osg::Texture2D();
                skyDomeTexture->setImage( skyDomeImage.get() );

                skyDomeTexture->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::MIRROR );
                skyDomeTexture->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::MIRROR );

                skyDomeTexture->setMaxAnisotropy( 8.0f );

                skyDomeStateSet->setTextureAttributeAndModes( 0, skyDomeTexture.get(), osg::StateAttribute::ON );
            }
            else
            {
                cerr << "ERROR! Cannot open texture file: " << textureFile << endl;
            }
        }
    }
    else
    {
        cerr << "ERROR! Wrong node pointer." << endl;
    }

    m_textureId_prev = m_textureId;
}
#else
////////////////////////////////////////////////////////////////////////////////

void DataTypeSkyDome::updateEphemeris()
{
    if ( m_ephemerisModel.valid() )
    {
        m_ephemerisModel->setLatitudeLongitudeAltitude( m_lat, m_lon, m_alt );

        if ( 0 == memcmp( &m_dateTime_prev, &m_dateTime, sizeof(Utils::DateTime) ) )
        {
            osgEphemeris::DateTime dateTime( (uint32_t)m_dateTime.year,
                                             (uint32_t)m_dateTime.month,
                                             (uint32_t)m_dateTime.day,
                                             (uint32_t)m_dateTime.hour,
                                             (uint32_t)m_dateTime.minute,
                                             (uint32_t)m_dateTime.second );

            dateTime.setTimeZoneOffset( false, 0 );

            m_ephemerisModel->setDateTime( dateTime );
        }
    }
    else
    {
        cerr << "ERROR! Wrong node pointer." << endl;
    }

    m_dateTime_prev = m_dateTime;
}

#endif

